1 Exemplo: altura e peso

Pessoas mais altas tendem a ser mais pesadas?

Temos dados de \(80\) alunos e alunas, com alturas em cm e pesos em kg:

  • O gráfico de dispersão (scatterplot) é o ideal para visualizar a correlação entre duas variáveis numéricas:

    peso_altura %>% 
      ggplot(aes(altura, peso)) +
        geom_point() +
        labs(
          x = 'altura (cm)',
          y = 'peso\n(kg)'
        )

2 Correlação linear

  • Vamos subtrair, de cada peso, a média dos pesos; vamos subtrair, de cada altura, a média das alturas.

    peso_altura <- peso_altura %>% 
      mutate(
        altura_desvio = altura - mean(altura),
        peso_desvio = peso - mean(peso)
      )
  • O novo scatterplot tem a mesma forma. Só mudam as escalas:

    peso_altura %>% 
      ggplot(aes(altura_desvio, peso_desvio)) +
        geom_point() +
        labs(
          x = 'desvios altura (cm)',
          y = 'desvios peso\n(kg)'
        )

  • Quando o desvio da altura e o desvio do peso têm o mesmo sinal, os valores têm uma associação positiva: ou altura e peso estão ambos acima da média, ou ambos abaixo da média.

  • Colorindo os pontos de acordo com a associação:

    peso_altura %>% 
      ggplot(aes(altura_desvio, peso_desvio)) +
        geom_point(
          data = . %>% filter(altura_desvio * peso_desvio >= 0),
          color = 'blue'
        ) +
        geom_point(
          data = . %>% filter(altura_desvio * peso_desvio < 0),
          color = 'red'
        ) +
        geom_hline(aes(yintercept = 0), linetype = 'dashed') +
        geom_vline(aes(xintercept = 0), linetype = 'dashed') +
        labs(
          x = 'desvios altura (cm)',
          y = 'desvios peso\n(kg)'
        )

3 Covariância

  • Examine o código acima: para verificar se os desvios têm o mesmo sinal, basta verificar se o produto dos desvios é positivo.

  • E mais: quanto maior o produto dos desvios, mais acima (ou mais abaixo) das respectivas médias os desvios estão, ao mesmo tempo. Ou seja, maior a associação entre as duas variáveis.

  • Estamos falando de

    \[ \text{produto dos desvios}_i = (\text{altura}_i - E(\text{altura})) \cdot (\text{peso}_i - E(\text{peso})) \]

  • Podemos calcular os produtos dos desvios e achar a média destes produtos. O resultado vai ser a covariância:

    \[ \text{Cov}(\text{altura}, \text{peso}) = \frac{ \sum\; [(\text{altura}_i - E(\text{altura})) \cdot (\text{peso}_i - E(\text{peso}))] }{n} \]

  • Ou, de forma mais compacta,

    \[ \text{Cov}(\text{altura}, \text{peso}) = E[(\text{altura} - E(\text{altura})) \cdot (\text{peso} - E(\text{peso}))] \]

  • O R usa \(n - 1\) no denominador, em vez de \(n\). Isto significa que o R calcula a covariância amostral, que serve como um estimador da covariância populacional.

  • Calculando:

    peso_altura %>% 
      mutate(produto = altura_desvio * peso_desvio) %>% 
      summarize(cov = sum(produto)/(nrow(.) - 1)) %>% 
      pull(cov)
    ## [1] 73,8935

3.1 Em R

cov(peso_altura$altura, peso_altura$peso)
## [1] 73,8935

3.2 Exercícios

  • Qual é a unidade da covariância? Examine as fórmulas acima para responder.

  • Execute o bloco abaixo com diversos valores de k, inclusive valores negativos.

    k <- 10
    
    X <- 1:5
    Y <- k * X
    
    tibble(X, Y) %>% 
      ggplot(aes(X, Y)) +
        geom_point(size = 2) +
        labs(
          title = paste('cov(X, Y) =', cov(X, Y))
        )

  • Qual a covariância máxima entre duas variáveis? E a mínima?

  • Usando as propriedades do valor esperado, mostre que, para quaisquer variáveis aleatórias \(X\) e \(Y\)

    \[ \text{Cov}(X,Y) \quad=\quad E\big[(X - E(X)) \cdot (Y - E(Y))\big] \quad=\quad E(XY) - E(X)E(Y) \]

  • Lembre-se de que a variância de uma variável aleatória \(X\) pode ser escrita como

    \[ \text{Var}(X) = E(X^2) - [E(X)]^2 \]

    Alguma semelhança entre esta expressão e a expressão do item anterior?

  • Qual a covariância de uma variável aleatória \(X\) com ela mesma?

  • O R usa \(n - 1\) no denominador da covariância. Mostre que definir a covariância como \[ \text{Cov}(X, Y) = \frac{1}{n - 1} \cdot \sum_{i = 1}^n \big[(x_i - E(X)) \cdot (y_i - E(Y)) \big] \]

    é equivalente a

    \[ \text{Cov}(X, Y) = \frac{n}{n-1} \cdot \big[ E(XY) - E(X)E(Y) \big] \]

  • Usando o data frame peso_altura, compute o valor da expressão acima e compare com o resultado de cov(peso_altura$altura, peso_altura$peso).

4 Coeficiente de correlação linear

  • Vamos padronizar as alturas e pesos (subtrair a média e dividir pelo desvio padrão):

    peso_altura <- peso_altura %>% 
      mutate(
        altura_padronizada = scale(altura),
        peso_padronizado = scale(peso)
      )
  • E construir o scatterplot:

    peso_altura %>% 
      ggplot(aes(altura_padronizada, peso_padronizado)) +
        geom_point(
          data = . %>% filter(altura_padronizada * peso_padronizado >= 0),
          color = 'blue'
        ) +
        geom_point(
          data = . %>% filter(altura_padronizada * peso_padronizado < 0),
          color = 'red'
        ) +
        geom_hline(aes(yintercept = 0), linetype = 'dashed') +
        geom_vline(aes(xintercept = 0), linetype = 'dashed') +
        labs(
          x = 'altura padronizada',
          y = 'peso\npadronizado'
        )

  • Ou seja, agora as variáveis são

    \[ Z_A = \frac{\text{altura} - E(\text{altura})}{DP(\text{altura})} \]

    e

    \[ Z_P = \frac{\text{peso} - E(\text{peso})}{DP(\text{peso})} \]

  • A covariância entre elas é

    cov(peso_altura$peso_padronizado, peso_altura$altura_padronizada)
    ##           [,1]
    ## [1,] 0,6440311
  • Quando padronizamos as duas variáveis, a covariância entre elas se chama correlação.

  • O coeficiente de correlação amostral é este valor.

    \[ r = \frac{\sum x_i y_i}{n - 1} \]

    onde \(X\) e \(Y\) são variáveis padronizadas.

  • Alguns livros chamam o coeficiente de correlação de \(\rho\) em vez de \(r\).

  • O coeficiente de correlação sempre está entre \(-1\) e \(1\), inclusive.

  • Fórmulas alternativas para o coeficiente de correlação entre \(X\) e \(Y\) (não necessariamente padronizadas):

    \[ r = \frac{\text{Cov}(X, Y)}{DP_X \cdot DP_Y} \]

    e

    \[ r = \frac{ \sum(x_i - E(X))\cdot(y_i - E(Y)) }{ \sqrt{\sum(x_i - E(X))^2\cdot(y_i - E(Y))^2} } \]

4.1 Em R

cor(peso_altura$peso, peso_altura$altura)
## [1] 0,6440311

4.2 Exercícios

  • Qual a unidade do coeficiente de correlação?

  • Execute o bloco abaixo com diversos valores de k, inclusive valores negativos.

    k <- 10
    
    X <- 1:5
    Y <- k * X
    
    tibble(X, Y) %>% 
      ggplot(aes(X, Y)) +
        geom_point(size = 2) +
        labs(
          title = paste('cor(X, Y) =', cor(X, Y))
        )

  • Como você explica os valores de \(\text{cor}(X,Y)\) no item anterior, em comparação com os valores de \(\text{cov}(X,Y)\) nesse outro exercício?

  • Qual a correlação de uma variável aleatória \(X\) com ela mesma?

  • Usando o data frame peso_altura, compute o valor do coeficiente de correlação entre peso e altura usando as fórmulas alternativas.

4.3 Observações importantes

A correlação é linear

  • Se \(r > 0\), valores altos de uma variável tendem a corresponder a valores altos da outra variável.

  • Se \(r < 0\), valores altos de uma variável tendem a corresponder a valores baixos da outra variável.

  • Se \(r = 0\), não há correlação linear entre as variáveis.

  • O coeficiente de correlação \(r\) só mede a correlação linear entre duas variáveis.

  • Exemplos de Anscombe, com \(4\) conjuntos de dados com o mesmo valor de \(r\) mas com associações muito diferentes entre as variáveis:

    anscombe
    desenhar <- function(n) {
    
      nomex <- paste0('x', n)
      nomey <- paste0('y', n)
      r <- cor(anscombe[[nomex]], anscombe[[nomey]]) %>% round(2)
    
      anscombe %>% 
        ggplot(aes(.data[[nomex]], .data[[nomey]])) +
          geom_point() +
          geom_smooth(method = 'lm', se = FALSE) +
          labs(
            title = paste('r =', r)
          )
    
    }
    
    
    plots <- map(1:4, desenhar)
    plots[[1]] + plots[[2]] + plots[[3]] + plots[[4]] 

  • Conclusões:

    • O valor de \(r\) só reflete correlação linear.

    • A presença de outliers produz valores não-confiáveis de \(r\).

Correlação \(\neq\) causação

  • Dados coletados em Oldenburg, na Alemanha, nos anos \(1930\), mostrando a correlação entre a quantidade de cegonhas e a população (de pessoas) na cidade:

    df <- read_csv('data/Storks.csv') %>% 
      transmute(
        cegonhas = Storks,
        pessoas = Population
      ) 
    
    r <- cor(df$cegonhas, df$pessoas) %>% round(2)
    
    df %>% 
      ggplot(aes(cegonhas, pessoas)) +
        geom_point() +
        labs(
          title = paste('r =', r)
        )

  • Variável oculta: quantidade de casas com chaminé.

  • Ou coincidência: http://tylervigen.com/spurious-correlations

5 Teste e intervalo de confiança para a correlação

5.1 Exemplo: PIB e CO\(_2\)

  • Em uma amostra de \(10\) países, examinamos o PIB (em trilhões de dólares) e a quantidade de emissões de CO\(_2\) (em milhões de toneladas):

  • Gráfico:

    df %>% 
      ggplot(aes(PIB, emissões)) +
        geom_point() +
        scale_y_continuous(
          'emissões\n(milhões de\ntoneladas)',
          breaks = seq(0, 1200, 200),
          limits = c(0, 1200)
        ) +
        scale_x_continuous(
          'PIB (trilhões US$)',
          breaks = 0:6,
          limits = c(0, 6)
        )

  • O coeficiente de correlação amostral é

    cor(df$PIB, df$emissões)
    ## [1] 0,9115924
  • Como esta é uma amostra, devemos calcular um intervalo de confiança para o coeficiente de correlação populacional.

  • O R faz isto com a função cor.test:

    ct <- cor.test(df$PIB, df$emissões)
    ct
    ## 
    ##  Pearson's product-moment correlation
    ## 
    ## data:  df$PIB and df$emissões
    ## t = 6,272, df = 8, p-value = 0,0002399
    ## alternative hypothesis: true correlation is not equal to 0
    ## 95 percent confidence interval:
    ##  0,6618340 0,9791965
    ## sample estimates:
    ##       cor 
    ## 0,9115924
  • Conclusão: com \(95\%\) de confiança, estimamos que o coeficiente de correlação populacional é capturado pelo intervalo

    \[ [\quad 0{,}661834 \quad ;\quad 0{,}9791965 \quad] \]

5.2 Exercícios

  • Qual é o tipo de teste usado por cor.test?

  • Quais são as hipóteses do teste?

  • Como é calculado o valor da estatística do teste (\(6{,}272\) no exemplo acima)?

  • Como é calculado o valor \(p\) (\(0{,}0002399\) no exemplo acima)?

  • Implemente uma função em R para fazer o mesmo que cor.test.

6 Transformações

6.1 Exemplo: fotografia

  • Ao configurar uma câmera para tirar uma foto, você deve ajustar a velocidade do obturador e a abertura do diafragma.

  • O fabricante de uma câmera recomenda os seguintes valores:

  • Coeficiente de correlação:

    cor(df$velocidade, df$abertura)
    ## [1] 0,978669
  • Gráfico:

    df %>% 
      ggplot(aes(velocidade, abertura)) +
        geom_point()

  • Vamos transformar os valores de uma das variáveis para tornar a correlação mais linear (menos curva):

    df <- df %>% 
      mutate(quad_abertura = abertura^2)
    df %>% 
      ggplot(aes(velocidade, quad_abertura)) +
        geom_point() +
        labs(y = 'abertura²')

  • A correlação se torna

    cor(df$velocidade, df$quad_abertura)
    ## [1] 0,9983046

6.2 Funções usadas em transformações

  • \(f(x) = x^2\): útil quando os valores originais têm cauda longa à esquerda, ou quando os valores originais têm concavidade para baixo.

  • \(f(x) = \sqrt{x}\): útil para valores que representam contagens.

  • \(f(x) = \log x\): útil para valores positivos, que crescem de acordo com percentagens, como salários, populações etc. Como \(\log 0\) não é definido, você pode precisar acrescentar uma constante \(\varepsilon\) aos valores antes da transformação.

  • \(f(x) = -\frac1{\sqrt{x}}\): o sinal negativo mantém a ordenação dos valores.

  • \(f(x) = -\frac1x\): útil para razões, como km/h. O sinal negativo mantém a ordenação dos valores. Como \(y/0\) não é definido, você pode precisar acrescentar uma constante \(\varepsilon\) aos valores antes da transformação.

6.3 Exercício

  • Aplique as outras transformações da lista acima aos valores da variável abertura. Desenhe gráficos, calcule correlações, e compare os resultados.

6.4 Exemplo: planetas

  • A distância média de um planeta ao Sol, em milhões de km, está correlacionada com a posição do planeta na sequência, mas como?

  • Coeficiente de correlação:

    cor(planetas$posição, planetas$distância)
    ## [1] 0,9102565
  • Gráfico:

    planetas %>% 
      ggplot(aes(posição, distância)) +
        geom_point() +
        scale_x_continuous(breaks = 1:9) +
        labs(
          y = 'distância\n(milhões km)'
        )

  • Vamos transformar os valores das distâncias:

    planetas <- planetas %>% 
      mutate(ldist = log10(distância))
    planetas %>% 
      ggplot(aes(posição, ldist)) +
        geom_point() +
        scale_x_continuous(breaks = 1:9) +
        labs(
          y = 'log distância\n(milhões km)'
        )

  • A correlação se torna

    cor(planetas$posição, planetas$ldist)
    ## [1] 0,990712

6.5 Exercício

  • Aplique as outras transformações da lista acima aos valores da variável distância. Desenhe gráficos, calcule correlações, e compare os resultados.
LS0tCnRpdGxlOiAnQ29ycmVsYcOnw6NvIGVudHJlIGR1YXMgdmFyacOhdmVpcycKc3VidGl0bGU6ICdQcm9iZXN0JwphdXRob3I6ICdmbmF1ZmVsJwplbWFpbDogJ2h0dHBzOi8vZm5hdWZlbC5naXRodWIuaW8vJwpkYXRlOiAnICh2LiBgciBmb3JtYXQoU3lzLkRhdGUoKSwgIiVkLyVtLyVZIilgKScKbGFuZzogJ3B0JwpvdXRwdXQ6IAogIHJtZGZvcm1hdDo6Zm5hdWZlbF9ybWRfZm9ybWF0OgogICAgdGhlbWU6IHJlYWRhYmxlICMgaHR0cHM6Ly9ib290c3dhdGNoLmNvbS8zL3JlYWRhYmxlLwogICAgaGlnaGxpZ2h0OiBweWdtZW50CiAgICB0b2M6IHRydWUKICAgIHRvY19kZXB0aDogMgogICAgdG9jX2Zsb2F0OiB0cnVlCiAgICBudW1iZXJfc2VjdGlvbnM6IHRydWUKICAgIGZpZ19jYXB0aW9uOiB0cnVlCiAgICBkZl9wcmludDogcGFnZWQKICAgIHNlbGZfY29udGFpbmVkOiB0cnVlCiAgICBjb2RlX2Rvd25sb2FkOiB0cnVlCiAgICBjb2RlX2ZvbGRpbmc6IHNob3cKICAgIGtlZXBfbWQ6IGZhbHNlCiAgICAjIGluY2x1ZGVzOgogICAgIyAgIGluX2hlYWRlcjogaGVhZGVyLmh0bWwKICAgICMgICBiZWZvcmVfYm9keTogZG9jX3ByZWZpeC5odG1sCiAgICAjICAgYWZ0ZXJfYm9keTogZG9jX3N1ZmZpeC5odG1sCi0tLQoKYGBge3Igc2V0dXAsIGluY2x1ZGU9RkFMU0V9CmxpYnJhcnkoa25pdHIpCgpvcHRzX2NodW5rJHNldCgKICBlY2hvID0gVFJVRSwgCiAgIyBjb2xsYXBzZSA9IFRSVUUsCiAgIyBjYWNoZSA9IFRSVUUsCiAgb3V0LndpZHRoID0gIjc1JSIsCiAgZmlnLmFsaWduID0gJ2NlbnRlcicsCiAgZmlnLndpZHRoID0gNiwKICBmaWcuc2hvdyA9ICJob2xkIgopCgpvcHRpb25zKGRwbHlyLnByaW50X21pbiA9IDYsIGRwbHlyLnByaW50X21heCA9IDYpCgojIFN1cHJlc3MgY3JheW9uIG91dHB1dApvcHRpb25zKGNyYXlvbi5lbmFibGVkID0gRkFMU0UpCgojIFVzZWZ1bCBsaWJyYXJpZXMKbGlicmFyeShnbHVlKQpsaWJyYXJ5KHBhdGNod29yaykKbGlicmFyeShsYXRleDJleHApCmxpYnJhcnkoa2FibGVFeHRyYSkKCiMgRm9yIG5pY2UgZGF0YWZyYW1lIHN1bW1hcmllcwpsaWJyYXJ5KHN1bW1hcnl0b29scykKc3Rfb3B0aW9ucygKICBwbGFpbi5hc2NpaSA9IEZBTFNFLAogIGRmU3VtbWFyeS52YXJudW1iZXJzID0gRkFMU0UsCiAgZGZTdW1tYXJ5LnN0eWxlID0gJ2dyaWQnLAogIGRmU3VtbWFyeS5ncmFwaC5tYWduaWYgPSAuNzUKKQoKIyBUaWR5IQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMgU29iZXIgdGhlbWUgZm9yIGdncGxvdAp0aGVtZV9zZXQoCiAgdGhlbWVfbGluZWRyYXcoKSArICAgICAgICAgICAgICAgICAgICAgICAgICMgU2V0IHNpbXBsZSB0aGVtZSBmb3IgZ2dwbG90CiAgICB0aGVtZSggICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICMgd2l0aCBzb21lIHR3ZWFrcwogICAgICBheGlzLnRpdGxlLnkubGVmdCA9IGVsZW1lbnRfdGV4dCgKICAgICAgICAgYW5nbGUgPSAwLCAgICAgICAgICAgICAgICAgICAgICAgICAgIyBOZXZlciByb3RhdGUgeSBheGlzIHRpdGxlCiAgICAgICAgIG1hcmdpbiA9IG1hcmdpbihyID0gMjApLCAgICAgICAgICAgICMgU2VwYXJhdGUgeSBheGlzIHRpdGxlIGEgbGl0dGxlIAogICAgICAgICB2anVzdCA9IC41ICAgICAgICAgICAgICAgICAgICAgICAgICAjIExlYXZlIHkgYXhpcyB0aXRsZSBpbiB0aGUgbWlkZGxlCiAgICAgICksCiAgICAgIGF4aXMudGl0bGUueC5ib3R0b20gPSBlbGVtZW50X3RleHQoCiAgICAgICAgIG1hcmdpbiA9IG1hcmdpbih0ID0gMjApICAgICAgICAgICAgICMgU2VwYXJhdGUgeCBheGlzIHRpdGxlIGEgbGl0dGxlIAogICAgICApLAogICAgICBheGlzLmxpbmUgPSBlbGVtZW50X2JsYW5rKCksICAgICAgICAgICAjIE5vIGF4aXMgbGluZXMKICAgICAgcGFuZWwuYm9yZGVyID0gZWxlbWVudF9ibGFuaygpLCAgICAgICAgIyBObyBmcmFtZQogICAgICBwYW5lbC5ncmlkLm1pbm9yID0gZWxlbWVudF9ibGFuaygpICAgICAjIE5vIGdyaWQgbWlub3IgbGluZXMKICAgICkKKQoKIyBBdm9pZCBzY2llbnRpZmljIG5vdGF0aW9uIGFuZCB1c2UgYSBjb21tYSBhcyBkZWNpbWFsIHNlcGFyYXRvcgpvcHRpb25zKAogIHNjaXBlbiA9IDE1LAogIE91dERlYyA9ICcsJwopCgojIEZvcm1hdCBhIG51bWJlciB3aXRoIHRob3VzYW5kIHNlcGFyYXRvcnMgKGRlZmF1bHQgcG9pbnQpCiMgYW5kIGRlY2ltYWwgY29tbWEgZW5jbG9zZWQgaW4gY3VybHkgYnJhY2VzIGZvciBMYVRlWCBwcmludGluZy4KIyBDQVJFRlVMOiBpZiBjYWxsZWQgb3V0c2lkZSBtYXRoIG1vZGUsIHdpbGwgcHJpbnQgdGhlIGJyYWNlcyEKZm0gPC0gZnVuY3Rpb24oeCwgYmlnID0gJy4nLCBkZWNpbWFsID0gJ3ssfScsIC4uLikgewogIGlmICghaXMubnVtZXJpYyh4KSkgewogICAgeAogIH0gZWxzZSB7CiAgICBwcmV0dHlOdW0oeCwgYmlnLm1hcmsgPSBiaWcsIGRlY2ltYWwubWFyayA9IGRlY2ltYWwsIC4uLikKICB9Cgp9CgojIFNldCB0aGlzIGFzIGEgaG9vayBmb3IgaW5saW5lIFIgY29kZQprbml0cjo6a25pdF9ob29rcyRzZXQoaW5saW5lID0gZm0pCgoKIyBUbyBjZW50ZXIgdGhlIHJlc3VsdHMgb2YgYSBjaHVuayAoaW1hZ2UsIHZpZGVvIGV0Yy4pCiMgVXNhZ2U6IAojICAgICAgICAgb3V0LmV4dHJhPWNlbnRlcigpCiMgICAgICAgICAKY2VudGVyIDwtIGZ1bmN0aW9uKCl7CiAgCiAgaWYgKGlzX2h0bWxfb3V0cHV0KCkpIHsKICAgICdjbGFzcz0iY2VudGVyIicKICB9CiAgCn0KCgojIFRvIGVtYmVkIFlUIHZpZGVvcyBpbiBIVE1MIGFuZCB0aGUgbGluayAoY2VudGVyZWQpIGluIExhVGVYCmVtYmVkX3l0IDwtIGZ1bmN0aW9uKGNvZGUpIHsKCiAgaWYgKGlzX2h0bWxfb3V0cHV0KCkpIHsKICAgIGluY2x1ZGVfdXJsKAogICAgICBwYXN0ZTAoCiAgICAgICAgJ2h0dHBzOi8vd3d3LnlvdXR1YmUuY29tL2VtYmVkLycsCiAgICAgICAgY29kZQogICAgICApCiAgICApCiAgfSBlbHNlIHsKICAgIGNhdCgKICAgICAgcGFzdGUwKAogICAgICAgICdgYGB7PWxhdGV4fVxuJywKICAgICAgICAnXFxiZWdpbntjZW50ZXJ9IFxcdXJse2h0dHBzOi8veW91dHUuYmUvJywKICAgICAgICBjb2RlLAogICAgICAgICd9IFxcZW5ke2NlbnRlcn1cbicsCiAgICAgICAgJ2BgYCcKICAgICAgKQogICAgKQogIH0KICAKfQoKYGBgCgpgYGB7anMgamF2YXNjcmlwdC1pbml0LCBlY2hvPUZBTFNFfQoKLy8gTWFrZSBvZmYtc2l0ZSBsaW5rcyBvcGVuIGluIGEgbmV3IHdpbmRvdy90YWIKZnVuY3Rpb24gY2hhbmdlVGFyZ2V0cygpIHsKICAkKCJhIikuYXR0cigKICAgICJ0YXJnZXQiLCBmdW5jdGlvbigpIHsKICAgICAgLy8gTG9hZCBsb2NhbCBsaW5rcyBsb2NhbGx5CiAgICAgIGlmICh0aGlzLmhvc3QgPT0gbG9jYXRpb24uaG9zdCkgcmV0dXJuICJfc2VsZiIKICAgICAgLy8gTG9hZCBvZmYtc2l0ZSBsaW5rcyBpbiBhIG5ldyB3aW5kb3cKICAgICAgZWxzZSByZXR1cm4gIl9ibGFuayI7CiAgICB9CiAgKTsKfQoKLy8gRXhlY3V0ZSB3aGVuIGRvY3VtZW50IGlzIHJlYWR5CiQoCiAgY2hhbmdlVGFyZ2V0cwopCmBgYAoKCiMgRXhlbXBsbzogYWx0dXJhIGUgcGVzbwoKYGBge3IgZWNobz1GQUxTRSwgbWVzc2FnZT1GQUxTRSwgY2FjaGU9VFJVRX0KcGVzb19hbHR1cmEgPC0gcmVhZF9jc3YoJ2RhdGEvSGVpZ2h0c19hbmRfV2VpZ2h0cy5jc3YnKSAlPiUgCiAgbXV0YXRlKAogICAgYWx0dXJhID0gSGVpZ2h0ICogMi41NCwKICAgIHBlc28gPSBXZWlnaHQgKiAwLjQ1CiAgKQpgYGAKCjo6OiB7LnJtZGJveH0KClBlc3NvYXMgbWFpcyBhbHRhcyB0ZW5kZW0gYSBzZXIgbWFpcyBwZXNhZGFzPwoKVGVtb3MgZGFkb3MgZGUgJGByIG5yb3cocGVzb19hbHR1cmEpYCQgYWx1bm9zIGUgYWx1bmFzLCBjb20gYWx0dXJhcyBlbSBjbSBlIHBlc29zIGVtIGtnOgoKYGBge3IgZWNobz1GQUxTRX0KcGVzb19hbHR1cmEgJT4lIHNlbGVjdChhbHR1cmEsIHBlc28pCmBgYAoKOjo6CgoqIE8gZ3LDoWZpY28gZGUgZGlzcGVyc8OjbyAoKnNjYXR0ZXJwbG90Kikgw6kgbyBpZGVhbCBwYXJhIHZpc3VhbGl6YXIgYSBjb3JyZWxhw6fDo28gZW50cmUgZHVhcyB2YXJpw6F2ZWlzIG51bcOpcmljYXM6CgogICAgYGBge3J9CiAgICBwZXNvX2FsdHVyYSAlPiUgCiAgICAgIGdncGxvdChhZXMoYWx0dXJhLCBwZXNvKSkgKwogICAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgbGFicygKICAgICAgICAgIHggPSAnYWx0dXJhIChjbSknLAogICAgICAgICAgeSA9ICdwZXNvXG4oa2cpJwogICAgICAgICkKICAgIGBgYAoKCiMgQ29ycmVsYcOnw6NvIGxpbmVhcgoKKiBWYW1vcyBzdWJ0cmFpciwgZGUgY2FkYSBwZXNvLCBhIG3DqWRpYSBkb3MgcGVzb3M7IHZhbW9zIHN1YnRyYWlyLCBkZSBjYWRhIGFsdHVyYSwgYSBtw6lkaWEgZGFzIGFsdHVyYXMuCgogICAgYGBge3J9CiAgICBwZXNvX2FsdHVyYSA8LSBwZXNvX2FsdHVyYSAlPiUgCiAgICAgIG11dGF0ZSgKICAgICAgICBhbHR1cmFfZGVzdmlvID0gYWx0dXJhIC0gbWVhbihhbHR1cmEpLAogICAgICAgIHBlc29fZGVzdmlvID0gcGVzbyAtIG1lYW4ocGVzbykKICAgICAgKQogICAgYGBgCgoqIE8gbm92byAqc2NhdHRlcnBsb3QqIHRlbSBhIG1lc21hIGZvcm1hLiBTw7MgbXVkYW0gYXMgZXNjYWxhczoKCiAgICBgYGB7cn0KICAgIHBlc29fYWx0dXJhICU+JSAKICAgICAgZ2dwbG90KGFlcyhhbHR1cmFfZGVzdmlvLCBwZXNvX2Rlc3ZpbykpICsKICAgICAgICBnZW9tX3BvaW50KCkgKwogICAgICAgIGxhYnMoCiAgICAgICAgICB4ID0gJ2Rlc3Zpb3MgYWx0dXJhIChjbSknLAogICAgICAgICAgeSA9ICdkZXN2aW9zIHBlc29cbihrZyknCiAgICAgICAgKQogICAgYGBgCgoqIFF1YW5kbyBvIGRlc3ZpbyBkYSBhbHR1cmEgZSBvIGRlc3ZpbyBkbyBwZXNvIHTDqm0gbyBbbWVzbW8gc2luYWxdey5obH0sIG9zIHZhbG9yZXMgdMOqbSB1bWEgW2Fzc29jaWHDp8OjbyBwb3NpdGl2YV17LmhsfTogb3UgYWx0dXJhIGUgcGVzbyBlc3TDo28gYW1ib3MgYWNpbWEgZGEgbcOpZGlhLCBvdSBhbWJvcyBhYmFpeG8gZGEgbcOpZGlhLgoKKiBDb2xvcmluZG8gb3MgcG9udG9zIGRlIGFjb3JkbyBjb20gYSBhc3NvY2lhw6fDo286CgogICAgYGBge3J9CiAgICBwZXNvX2FsdHVyYSAlPiUgCiAgICAgIGdncGxvdChhZXMoYWx0dXJhX2Rlc3ZpbywgcGVzb19kZXN2aW8pKSArCiAgICAgICAgZ2VvbV9wb2ludCgKICAgICAgICAgIGRhdGEgPSAuICU+JSBmaWx0ZXIoYWx0dXJhX2Rlc3ZpbyAqIHBlc29fZGVzdmlvID49IDApLAogICAgICAgICAgY29sb3IgPSAnYmx1ZScKICAgICAgICApICsKICAgICAgICBnZW9tX3BvaW50KAogICAgICAgICAgZGF0YSA9IC4gJT4lIGZpbHRlcihhbHR1cmFfZGVzdmlvICogcGVzb19kZXN2aW8gPCAwKSwKICAgICAgICAgIGNvbG9yID0gJ3JlZCcKICAgICAgICApICsKICAgICAgICBnZW9tX2hsaW5lKGFlcyh5aW50ZXJjZXB0ID0gMCksIGxpbmV0eXBlID0gJ2Rhc2hlZCcpICsKICAgICAgICBnZW9tX3ZsaW5lKGFlcyh4aW50ZXJjZXB0ID0gMCksIGxpbmV0eXBlID0gJ2Rhc2hlZCcpICsKICAgICAgICBsYWJzKAogICAgICAgICAgeCA9ICdkZXN2aW9zIGFsdHVyYSAoY20pJywKICAgICAgICAgIHkgPSAnZGVzdmlvcyBwZXNvXG4oa2cpJwogICAgICAgICkKICAgIGBgYAoKIyBDb3ZhcmnDom5jaWEKCiogRXhhbWluZSBvIGPDs2RpZ28gYWNpbWE6IHBhcmEgdmVyaWZpY2FyIHNlIG9zIGRlc3Zpb3MgdMOqbSBvIG1lc21vIHNpbmFsLCBiYXN0YSB2ZXJpZmljYXIgc2UgbyBwcm9kdXRvIGRvcyBkZXN2aW9zIMOpIHBvc2l0aXZvLgoKKiBFIG1haXM6IHF1YW50byBtYWlvciBvIHByb2R1dG8gZG9zIGRlc3Zpb3MsIG1haXMgYWNpbWEgKG91IG1haXMgYWJhaXhvKSBkYXMgcmVzcGVjdGl2YXMgbcOpZGlhcyBvcyBkZXN2aW9zIGVzdMOjbywgW2FvIG1lc21vIHRlbXBvXXsuaGx9LiBPdSBzZWphLCBtYWlvciBhIGFzc29jaWHDp8OjbyBlbnRyZSBhcyBkdWFzIHZhcmnDoXZlaXMuCgoqIEVzdGFtb3MgZmFsYW5kbyBkZQoKICAkJAogIFx0ZXh0e3Byb2R1dG8gZG9zIGRlc3Zpb3N9X2kgPSAoXHRleHR7YWx0dXJhfV9pIC0gRShcdGV4dHthbHR1cmF9KSkgXGNkb3QgKFx0ZXh0e3Blc299X2kgLSBFKFx0ZXh0e3Blc299KSkKICAkJAoKKiBQb2RlbW9zIGNhbGN1bGFyIG9zIHByb2R1dG9zIGRvcyBkZXN2aW9zIGUgYWNoYXIgYSBtw6lkaWEgZGVzdGVzIHByb2R1dG9zLiBPIHJlc3VsdGFkbyB2YWkgc2VyIGEgW2NvdmFyacOibmNpYTpdey5obH0KCiAgJCQKICBcdGV4dHtDb3Z9KFx0ZXh0e2FsdHVyYX0sIFx0ZXh0e3Blc299KSA9IAogIFxmcmFjewogIFxzdW1cOyBbKFx0ZXh0e2FsdHVyYX1faSAtIEUoXHRleHR7YWx0dXJhfSkpIFxjZG90IChcdGV4dHtwZXNvfV9pIC0gRShcdGV4dHtwZXNvfSkpXQogIH17bn0KICAkJAoKKiBPdSwgZGUgZm9ybWEgbWFpcyBjb21wYWN0YSwKCiAgJCQKICBcdGV4dHtDb3Z9KFx0ZXh0e2FsdHVyYX0sIFx0ZXh0e3Blc299KSA9IAogIEVbKFx0ZXh0e2FsdHVyYX0gLSBFKFx0ZXh0e2FsdHVyYX0pKSBcY2RvdCAoXHRleHR7cGVzb30gLSBFKFx0ZXh0e3Blc299KSldCiAgJCQKCiogW08gUiB1c2EgJG4gLSAxJCBubyBkZW5vbWluYWRvcixdey5obH0gZW0gdmV6IGRlICRuJC4gSXN0byBzaWduaWZpY2EgcXVlIG8gUiBjYWxjdWxhIGEgW2NvdmFyacOibmNpYSBhbW9zdHJhbF17LmhsfSwgcXVlIHNlcnZlIGNvbW8gdW0gZXN0aW1hZG9yIGRhIGNvdmFyacOibmNpYSBwb3B1bGFjaW9uYWwuCgoqIENhbGN1bGFuZG86CgogICAgYGBge3J9CiAgICBwZXNvX2FsdHVyYSAlPiUgCiAgICAgIG11dGF0ZShwcm9kdXRvID0gYWx0dXJhX2Rlc3ZpbyAqIHBlc29fZGVzdmlvKSAlPiUgCiAgICAgIHN1bW1hcml6ZShjb3YgPSBzdW0ocHJvZHV0bykvKG5yb3coLikgLSAxKSkgJT4lIAogICAgICBwdWxsKGNvdikKICAgIGBgYAoKCiMjIEVtIFIKCmBgYHtyfQpjb3YocGVzb19hbHR1cmEkYWx0dXJhLCBwZXNvX2FsdHVyYSRwZXNvKQpgYGAKCiMjIEV4ZXJjw61jaW9zCgoqIFF1YWwgw6kgYSB1bmlkYWRlIGRhIGNvdmFyacOibmNpYT8gRXhhbWluZSBhcyBmw7NybXVsYXMgYWNpbWEgcGFyYSByZXNwb25kZXIuCgoqIFtdeyNleGNvdn0gRXhlY3V0ZSBvIGJsb2NvIGFiYWl4byBjb20gZGl2ZXJzb3MgdmFsb3JlcyBkZSBga2AsIGluY2x1c2l2ZSB2YWxvcmVzIG5lZ2F0aXZvcy4KCiAgICBgYGB7cn0KICAgIGsgPC0gMTAKICAgIAogICAgWCA8LSAxOjUKICAgIFkgPC0gayAqIFgKICAgIAogICAgdGliYmxlKFgsIFkpICU+JSAKICAgICAgZ2dwbG90KGFlcyhYLCBZKSkgKwogICAgICAgIGdlb21fcG9pbnQoc2l6ZSA9IDIpICsKICAgICAgICBsYWJzKAogICAgICAgICAgdGl0bGUgPSBwYXN0ZSgnY292KFgsIFkpID0nLCBjb3YoWCwgWSkpCiAgICAgICAgKQogICAgYGBgCgoqIFF1YWwgYSBjb3ZhcmnDom5jaWEgbcOheGltYSBlbnRyZSBkdWFzIHZhcmnDoXZlaXM/IEUgYSBtw61uaW1hPwoKKiBVc2FuZG8gYXMgcHJvcHJpZWRhZGVzIGRvIHZhbG9yIGVzcGVyYWRvLCBtb3N0cmUgcXVlLCBwYXJhIHF1YWlzcXVlciB2YXJpw6F2ZWlzIGFsZWF0w7NyaWFzICRYJCBlICRZJAoKICAkJAogIFx0ZXh0e0Nvdn0oWCxZKSBccXVhZD1ccXVhZCBFXGJpZ1soWCAtIEUoWCkpIFxjZG90IChZIC0gRShZKSlcYmlnXSBccXVhZD1ccXVhZCBFKFhZKSAtIEUoWClFKFkpCiAgJCQKCiogTGVtYnJlLXNlIGRlIHF1ZSBhIHZhcmnDom5jaWEgZGUgdW1hIHZhcmnDoXZlbCBhbGVhdMOzcmlhICRYJCBwb2RlIHNlciBlc2NyaXRhIGNvbW8KCiAgJCQKICBcdGV4dHtWYXJ9KFgpID0gRShYXjIpIC0gW0UoWCldXjIKICAkJAoKICBBbGd1bWEgc2VtZWxoYW7Dp2EgZW50cmUgZXN0YSBleHByZXNzw6NvIGUgYSBleHByZXNzw6NvIGRvIGl0ZW0gYW50ZXJpb3I/CiAgCiogUXVhbCBhIGNvdmFyacOibmNpYSBkZSB1bWEgdmFyacOhdmVsIGFsZWF0w7NyaWEgJFgkIGNvbSBlbGEgbWVzbWE/CgoqIE8gUiB1c2EgJG4gLSAxJCBubyBkZW5vbWluYWRvciBkYSBjb3ZhcmnDom5jaWEuIE1vc3RyZSBxdWUgZGVmaW5pciBhIGNvdmFyacOibmNpYSBjb21vIAogICQkCiAgXHRleHR7Q292fShYLCBZKSA9IFxmcmFjezF9e24gLSAxfSBcY2RvdCBcc3VtX3tpID0gMX1ebiBcYmlnWyh4X2kgLSBFKFgpKSBcY2RvdCAoeV9pIC0gRShZKSkgXGJpZ10KICAkJAoKICDDqSBlcXVpdmFsZW50ZSBhCiAgCiAgJCQKICBcdGV4dHtDb3Z9KFgsIFkpID0gXGZyYWN7bn17bi0xfSBcY2RvdCBcYmlnWyBFKFhZKSAtIEUoWClFKFkpIFxiaWddCiAgJCQKCgoqIFVzYW5kbyBvICpkYXRhIGZyYW1lKiBgcGVzb19hbHR1cmFgLCBjb21wdXRlIG8gdmFsb3IgZGEgZXhwcmVzc8OjbyBhY2ltYSBlIGNvbXBhcmUgY29tIG8gcmVzdWx0YWRvIGRlIGBjb3YocGVzb19hbHR1cmEkYWx0dXJhLCBwZXNvX2FsdHVyYSRwZXNvKWAuCgoKIyBDb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28gbGluZWFyCgoqIFZhbW9zIHBhZHJvbml6YXIgYXMgYWx0dXJhcyBlIHBlc29zIChzdWJ0cmFpciBhIG3DqWRpYSBlIGRpdmlkaXIgcGVsbyBkZXN2aW8gcGFkcsOjbyk6CgogICAgYGBge3J9CiAgICBwZXNvX2FsdHVyYSA8LSBwZXNvX2FsdHVyYSAlPiUgCiAgICAgIG11dGF0ZSgKICAgICAgICBhbHR1cmFfcGFkcm9uaXphZGEgPSBzY2FsZShhbHR1cmEpLAogICAgICAgIHBlc29fcGFkcm9uaXphZG8gPSBzY2FsZShwZXNvKQogICAgICApCiAgICBgYGAKCiogRSBjb25zdHJ1aXIgbyAqc2NhdHRlcnBsb3QqOgoKICAgIGBgYHtyfQogICAgcGVzb19hbHR1cmEgJT4lIAogICAgICBnZ3Bsb3QoYWVzKGFsdHVyYV9wYWRyb25pemFkYSwgcGVzb19wYWRyb25pemFkbykpICsKICAgICAgICBnZW9tX3BvaW50KAogICAgICAgICAgZGF0YSA9IC4gJT4lIGZpbHRlcihhbHR1cmFfcGFkcm9uaXphZGEgKiBwZXNvX3BhZHJvbml6YWRvID49IDApLAogICAgICAgICAgY29sb3IgPSAnYmx1ZScKICAgICAgICApICsKICAgICAgICBnZW9tX3BvaW50KAogICAgICAgICAgZGF0YSA9IC4gJT4lIGZpbHRlcihhbHR1cmFfcGFkcm9uaXphZGEgKiBwZXNvX3BhZHJvbml6YWRvIDwgMCksCiAgICAgICAgICBjb2xvciA9ICdyZWQnCiAgICAgICAgKSArCiAgICAgICAgZ2VvbV9obGluZShhZXMoeWludGVyY2VwdCA9IDApLCBsaW5ldHlwZSA9ICdkYXNoZWQnKSArCiAgICAgICAgZ2VvbV92bGluZShhZXMoeGludGVyY2VwdCA9IDApLCBsaW5ldHlwZSA9ICdkYXNoZWQnKSArCiAgICAgICAgbGFicygKICAgICAgICAgIHggPSAnYWx0dXJhIHBhZHJvbml6YWRhJywKICAgICAgICAgIHkgPSAncGVzb1xucGFkcm9uaXphZG8nCiAgICAgICAgKQogICAgYGBgCgoqIE91IHNlamEsIGFnb3JhIGFzIHZhcmnDoXZlaXMgc8OjbwoKICAkJAogIFpfQSA9IFxmcmFje1x0ZXh0e2FsdHVyYX0gLSBFKFx0ZXh0e2FsdHVyYX0pfXtEUChcdGV4dHthbHR1cmF9KX0KICAkJAoKICBlCiAgCiAgJCQKICBaX1AgPSBcZnJhY3tcdGV4dHtwZXNvfSAtIEUoXHRleHR7cGVzb30pfXtEUChcdGV4dHtwZXNvfSl9CiAgJCQKCiogQSBjb3ZhcmnDom5jaWEgZW50cmUgZWxhcyDDqQoKICAgIGBgYHtyfQogICAgY292KHBlc29fYWx0dXJhJHBlc29fcGFkcm9uaXphZG8sIHBlc29fYWx0dXJhJGFsdHVyYV9wYWRyb25pemFkYSkKICAgIGBgYAoKKiBRdWFuZG8gcGFkcm9uaXphbW9zIGFzIGR1YXMgdmFyacOhdmVpcywgYSBjb3ZhcmnDom5jaWEgZW50cmUgZWxhcyBzZSBjaGFtYSBbY29ycmVsYcOnw6NvXXsuaGx9LgoKKiBPIFtjb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28gYW1vc3RyYWxdey5obH0gw6kgZXN0ZSB2YWxvci4KCiAgJCQKICByID0gXGZyYWN7XHN1bSB4X2kgeV9pfXtuIC0gMX0KICAkJAoKICBvbmRlIFskWCQgZSAkWSQgc8OjbyB2YXJpw6F2ZWlzIHBhZHJvbml6YWRhc117LmhsfS4KICAKKiBBbGd1bnMgbGl2cm9zIGNoYW1hbSBvIGNvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyBkZSAkXHJobyQgZW0gdmV6IGRlICRyJC4KCiogTyBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28gW3NlbXByZSBlc3TDoSBlbnRyZSAkLTEkIGUgJDEkXXsuaGx9LCBpbmNsdXNpdmUuCgoqIEbDs3JtdWxhcyBhbHRlcm5hdGl2YXMgcGFyYSBvIGNvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyBlbnRyZSAkWCQgZSAkWSQgKG7Do28gbmVjZXNzYXJpYW1lbnRlIHBhZHJvbml6YWRhcyk6CgogICQkCiAgciA9IFxmcmFje1x0ZXh0e0Nvdn0oWCwgWSl9e0RQX1ggXGNkb3QgRFBfWX0KICAkJAogIAogIGUKCiAgJCQKICByID0gXGZyYWN7CiAgICBcc3VtKHhfaSAtIEUoWCkpXGNkb3QoeV9pIC0gRShZKSkKICB9ewogICAgXHNxcnR7XHN1bSh4X2kgLSBFKFgpKV4yXGNkb3QoeV9pIC0gRShZKSleMn0KICB9CiAgJCQKCgojIyBFbSBSCgpgYGB7cn0KY29yKHBlc29fYWx0dXJhJHBlc28sIHBlc29fYWx0dXJhJGFsdHVyYSkKYGBgCgojIyBFeGVyY8OtY2lvcwoKKiBRdWFsIGEgdW5pZGFkZSBkbyBjb2VmaWNpZW50ZSBkZSBjb3JyZWxhw6fDo28/CgoqIEV4ZWN1dGUgbyBibG9jbyBhYmFpeG8gY29tIGRpdmVyc29zIHZhbG9yZXMgZGUgYGtgLCBpbmNsdXNpdmUgdmFsb3JlcyBuZWdhdGl2b3MuCgogICAgYGBge3J9CiAgICBrIDwtIDEwCiAgICAKICAgIFggPC0gMTo1CiAgICBZIDwtIGsgKiBYCiAgICAKICAgIHRpYmJsZShYLCBZKSAlPiUgCiAgICAgIGdncGxvdChhZXMoWCwgWSkpICsKICAgICAgICBnZW9tX3BvaW50KHNpemUgPSAyKSArCiAgICAgICAgbGFicygKICAgICAgICAgIHRpdGxlID0gcGFzdGUoJ2NvcihYLCBZKSA9JywgY29yKFgsIFkpKQogICAgICAgICkKICAgIGBgYAoKKiBDb21vIHZvY8OqIGV4cGxpY2Egb3MgdmFsb3JlcyBkZSAkXHRleHR7Y29yfShYLFkpJCBubyBpdGVtIGFudGVyaW9yLCBlbSBjb21wYXJhw6fDo28gY29tIG9zIHZhbG9yZXMgZGUgJFx0ZXh0e2Nvdn0oWCxZKSQgW25lc3NlIG91dHJvIGV4ZXJjw61jaW9dKCNleGNvdik/CgoqIFF1YWwgYSBjb3JyZWxhw6fDo28gZGUgdW1hIHZhcmnDoXZlbCBhbGVhdMOzcmlhICRYJCBjb20gZWxhIG1lc21hPwoKKiBVc2FuZG8gbyAqZGF0YSBmcmFtZSogYHBlc29fYWx0dXJhYCwgY29tcHV0ZSBvIHZhbG9yIGRvIGNvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyBlbnRyZSBwZXNvIGUgYWx0dXJhIHVzYW5kbyBhcyBmw7NybXVsYXMgYWx0ZXJuYXRpdmFzLgoKCiMjIE9ic2VydmHDp8O1ZXMgaW1wb3J0YW50ZXMKCiMjIyBBIGNvcnJlbGHDp8OjbyDDqSAqbGluZWFyKiB7LX0KCiFbXShpbWFnZXMvUGVhcnNvbl9Db3JyZWxhdGlvbl9Db2VmZmljaWVudF9hbmRfYXNzb2NpYXRlZF9zY2F0dGVycGxvdHMucG5nKXsgc3R5bGU9IndpZHRoOiA5MCU7IiAuY2VudGVyIH0KCjo6OntzdHlsZT0idGV4dC1hbGlnbjogcmlnaHQ7In0KKGh0dHBzOi8vY29tbW9ucy53aWtpbWVkaWEub3JnL3dpa2kvRmlsZTpQZWFyc29uX0NvcnJlbGF0aW9uX0NvZWZmaWNpZW50X2FuZF9hc3NvY2lhdGVkX3NjYXR0ZXJwbG90cy5wbmcpCjo6OgoKKiBTZSAkciA+IDAkLCB2YWxvcmVzIFthbHRvc117LmhsfSBkZSB1bWEgdmFyacOhdmVsIHRlbmRlbSBhIGNvcnJlc3BvbmRlciBhIHZhbG9yZXMgW2FsdG9zXXsuaGx9IGRhIG91dHJhIHZhcmnDoXZlbC4KCiogU2UgJHIgPCAwJCwgdmFsb3JlcyBbYWx0b3Ndey5obH0gZGUgdW1hIHZhcmnDoXZlbCB0ZW5kZW0gYSBjb3JyZXNwb25kZXIgYSB2YWxvcmVzIFtiYWl4b3Ndey5obH0gZGEgb3V0cmEgdmFyacOhdmVsLgoKKiBTZSAkciA9IDAkLCBuw6NvIGjDoSBjb3JyZWxhw6fDo28gW2xpbmVhcl17LmhsfSBlbnRyZSBhcyB2YXJpw6F2ZWlzLgoKKiBPIGNvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyAkciQgc8OzIG1lZGUgYSBjb3JyZWxhw6fDo28gW2xpbmVhcl17LmhsfSBlbnRyZSBkdWFzIHZhcmnDoXZlaXMuCgogICFbXShpbWFnZXMvQ29ycmVsYXRpb25fZXhhbXBsZXMyLnN2Zyl7IHN0eWxlPSJ3aWR0aDogOTAlOyIgLmNlbnRlciB9Cgo6Ojp7c3R5bGU9InRleHQtYWxpZ246IHJpZ2h0OyJ9CihodHRwczovL2NvbW1vbnMud2lraW1lZGlhLm9yZy93aWtpL0ZpbGU6Q29ycmVsYXRpb25fZXhhbXBsZXMyLnN2ZykKOjo6CgoqIFtFeGVtcGxvcyBkZSBBbnNjb21iZV0oaHR0cHM6Ly9wdC53aWtpcGVkaWEub3JnL3dpa2kvUXVhcnRldG9fZGVfQW5zY29tYmUpLCBjb20gJDQkIGNvbmp1bnRvcyBkZSBkYWRvcyBjb20gW28gbWVzbW8gdmFsb3IgZGUgJHIkXXsuaGx9IG1hcyBjb20gYXNzb2NpYcOnw7VlcyBbbXVpdG8gZGlmZXJlbnRlc117LmhsfSBlbnRyZSBhcyB2YXJpw6F2ZWlzOgoKICBgYGB7cn0KICBhbnNjb21iZQogIGBgYAoKICAgIGBgYHtyIG1lc3NhZ2U9RkFMU0UsIGNhY2hlPVRSVUV9CiAgICBkZXNlbmhhciA8LSBmdW5jdGlvbihuKSB7CiAgICAgIAogICAgICBub21leCA8LSBwYXN0ZTAoJ3gnLCBuKQogICAgICBub21leSA8LSBwYXN0ZTAoJ3knLCBuKQogICAgICByIDwtIGNvcihhbnNjb21iZVtbbm9tZXhdXSwgYW5zY29tYmVbW25vbWV5XV0pICU+JSByb3VuZCgyKQogICAgICAKICAgICAgYW5zY29tYmUgJT4lIAogICAgICAgIGdncGxvdChhZXMoLmRhdGFbW25vbWV4XV0sIC5kYXRhW1tub21leV1dKSkgKwogICAgICAgICAgZ2VvbV9wb2ludCgpICsKICAgICAgICAgIGdlb21fc21vb3RoKG1ldGhvZCA9ICdsbScsIHNlID0gRkFMU0UpICsKICAgICAgICAgIGxhYnMoCiAgICAgICAgICAgIHRpdGxlID0gcGFzdGUoJ3IgPScsIHIpCiAgICAgICAgICApCiAgICAgIAogICAgfQogICAgICAKICAgICAgCiAgICBwbG90cyA8LSBtYXAoMTo0LCBkZXNlbmhhcikKICAgIHBsb3RzW1sxXV0gKyBwbG90c1tbMl1dICsgcGxvdHNbWzNdXSArIHBsb3RzW1s0XV0gCiAgICBgYGAKCiogQ29uY2x1c8O1ZXM6CgogICogTyB2YWxvciBkZSAkciQgc8OzIHJlZmxldGUgY29ycmVsYcOnw6NvIFtsaW5lYXJdey5obH0uCiAgCiAgKiBBIHByZXNlbsOnYSBkZSBbKm91dGxpZXJzKl17LmhsfSBwcm9kdXogdmFsb3JlcyBuw6NvLWNvbmZpw6F2ZWlzIGRlICRyJC4KICAKICAKIyMjIENvcnJlbGHDp8OjbyAkXG5lcSQgY2F1c2HDp8OjbyB7LX0KCiogRGFkb3MgY29sZXRhZG9zIGVtIE9sZGVuYnVyZywgbmEgQWxlbWFuaGEsIG5vcyBhbm9zICQxOTMwJCwgbW9zdHJhbmRvIGEgY29ycmVsYcOnw6NvIGVudHJlIGEgcXVhbnRpZGFkZSBkZSBjZWdvbmhhcyBlIGEgcG9wdWxhw6fDo28gKGRlIHBlc3NvYXMpIG5hIGNpZGFkZToKCiAgICBgYGB7ciBtZXNzYWdlPUZBTFNFLCBjYWNoZT1UUlVFfQogICAgZGYgPC0gcmVhZF9jc3YoJ2RhdGEvU3RvcmtzLmNzdicpICU+JSAKICAgICAgdHJhbnNtdXRlKAogICAgICAgIGNlZ29uaGFzID0gU3RvcmtzLAogICAgICAgIHBlc3NvYXMgPSBQb3B1bGF0aW9uCiAgICAgICkgCiAgICAKICAgIHIgPC0gY29yKGRmJGNlZ29uaGFzLCBkZiRwZXNzb2FzKSAlPiUgcm91bmQoMikKICAgIAogICAgZGYgJT4lIAogICAgICBnZ3Bsb3QoYWVzKGNlZ29uaGFzLCBwZXNzb2FzKSkgKwogICAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgbGFicygKICAgICAgICAgIHRpdGxlID0gcGFzdGUoJ3IgPScsIHIpCiAgICAgICAgKQogICAgCiAgICBgYGAKCiogVmFyacOhdmVsIG9jdWx0YTogcXVhbnRpZGFkZSBkZSBjYXNhcyBjb20gY2hhbWluw6kuCgoqIE91IGNvaW5jaWTDqm5jaWE6IGh0dHA6Ly90eWxlcnZpZ2VuLmNvbS9zcHVyaW91cy1jb3JyZWxhdGlvbnMKCgojIFRlc3RlIGUgaW50ZXJ2YWxvIGRlIGNvbmZpYW7Dp2EgcGFyYSBhIGNvcnJlbGHDp8OjbwoKIyMgRXhlbXBsbzogUElCIGUgQ08kXzIkCgoqIEVtIHVtYSBhbW9zdHJhIGRlICQxMCQgcGHDrXNlcywgZXhhbWluYW1vcyBvIFBJQiAoZW0gdHJpbGjDtWVzIGRlIGTDs2xhcmVzKSBlIGEgcXVhbnRpZGFkZSBkZSBlbWlzc8O1ZXMgZGUgQ08kXzIkIChlbSBtaWxow7VlcyBkZSB0b25lbGFkYXMpOgoKICBgYGB7ciBlY2hvPUZBTFNFfQogIGRmIDwtIHRpYmJsZSgKICAgIFBJQiA9IGMoMS43LCAxLjIsIDIuNSwgMi44LCAzLjYsIDIuMiwgMC44LCAxLjUsIDIuNCwgNS45KSwKICAgIGVtaXNzw7VlcyA9IGMoCiAgICAgIDU1Mi42LCA0NjIuMywgNDc1LjQsIDM3NC4zLCA3NDguNSwgNDAwLjksIDI1My4wLCAzMTguNiwgNDk2LjgsIDExODAuNgogICAgKQogICkKICAKICBkZgogIGBgYAoKKiBHcsOhZmljbzoKCiAgICBgYGB7cn0KICAgIGRmICU+JSAKICAgICAgZ2dwbG90KGFlcyhQSUIsIGVtaXNzw7VlcykpICsKICAgICAgICBnZW9tX3BvaW50KCkgKwogICAgICAgIHNjYWxlX3lfY29udGludW91cygKICAgICAgICAgICdlbWlzc8O1ZXNcbihtaWxow7VlcyBkZVxudG9uZWxhZGFzKScsCiAgICAgICAgICBicmVha3MgPSBzZXEoMCwgMTIwMCwgMjAwKSwKICAgICAgICAgIGxpbWl0cyA9IGMoMCwgMTIwMCkKICAgICAgICApICsKICAgICAgICBzY2FsZV94X2NvbnRpbnVvdXMoCiAgICAgICAgICAnUElCICh0cmlsaMO1ZXMgVVMkKScsCiAgICAgICAgICBicmVha3MgPSAwOjYsCiAgICAgICAgICBsaW1pdHMgPSBjKDAsIDYpCiAgICAgICAgKQogICAgYGBgCgoqIE8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIFthbW9zdHJhbF17LmhsfSDDqQoKICAgIGBgYHtyfQogICAgY29yKGRmJFBJQiwgZGYkZW1pc3PDtWVzKQogICAgYGBgCgoqIENvbW8gZXN0YSDDqSB1bWEgYW1vc3RyYSwgZGV2ZW1vcyBjYWxjdWxhciB1bSBpbnRlcnZhbG8gZGUgY29uZmlhbsOnYSBwYXJhIG8gY29lZmljaWVudGUgZGUgY29ycmVsYcOnw6NvIFtwb3B1bGFjaW9uYWxdey5obH0uCgoqIE8gUiBmYXogaXN0byBjb20gYSBmdW7Dp8OjbyBgY29yLnRlc3RgOgoKICAgIGBgYHtyfQogICAgY3QgPC0gY29yLnRlc3QoZGYkUElCLCBkZiRlbWlzc8O1ZXMpCiAgICBjdAogICAgYGBgCgoqIENvbmNsdXPDo286IGNvbSAkOTVcJSQgZGUgY29uZmlhbsOnYSwgZXN0aW1hbW9zIHF1ZSBvIGNvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbyBbcG9wdWxhY2lvbmFsXXsuaGx9IMOpIGNhcHR1cmFkbyBwZWxvIGludGVydmFsbwoKICAkJAogIFtccXVhZCBgciBjdCRjb25mLmludFsxXWAgXHF1YWQgO1xxdWFkICBgciBjdCRjb25mLmludFsyXWAgXHF1YWRdCiAgJCQKCiMjIEV4ZXJjw61jaW9zCgoqIFF1YWwgw6kgbyB0aXBvIGRlIHRlc3RlIHVzYWRvIHBvciBgY29yLnRlc3RgPwoKKiBRdWFpcyBzw6NvIGFzIGhpcMOzdGVzZXMgZG8gdGVzdGU/CgoqIENvbW8gw6kgY2FsY3VsYWRvIG8gdmFsb3IgZGEgZXN0YXTDrXN0aWNhIGRvIHRlc3RlICgkYHIgY3Qkc3RhdGlzdGljICU+JSByb3VuZCg0KWAkIG5vIGV4ZW1wbG8gYWNpbWEpPwoKKiBDb21vIMOpIGNhbGN1bGFkbyBvIHZhbG9yICRwJCAoJGByIGN0JHAudmFsdWUgJT4lIHJvdW5kKDcpYCQgbm8gZXhlbXBsbyBhY2ltYSk/CgoqIEltcGxlbWVudGUgdW1hIGZ1bsOnw6NvIGVtIFIgcGFyYSBmYXplciBvIG1lc21vIHF1ZSBgY29yLnRlc3RgLgoKCiMgVHJhbnNmb3JtYcOnw7VlcwoKIyMgRXhlbXBsbzogZm90b2dyYWZpYQoKKiBBbyBjb25maWd1cmFyIHVtYSBjw6JtZXJhIHBhcmEgdGlyYXIgdW1hIGZvdG8sIHZvY8OqIGRldmUgYWp1c3RhciBhIHZlbG9jaWRhZGUgZG8gb2J0dXJhZG9yIGUgYSBhYmVydHVyYSBkbyBkaWFmcmFnbWEuCgoqIE8gZmFicmljYW50ZSBkZSB1bWEgY8OibWVyYSByZWNvbWVuZGEgb3Mgc2VndWludGVzIHZhbG9yZXM6CgogIGBgYHtyIGVjaG89RkFMU0V9CiAgZGYgPC0gdGliYmxlKAogICAgdmVsb2NpZGFkZSA9IGMoCiAgICAgIDEgLyAxMDAwLAogICAgICAxIC8gNTAwLAogICAgICAxIC8gMjUwLAogICAgICAxIC8gMTI1LAogICAgICAxIC8gNjAsCiAgICAgIDEgLyAzMCwKICAgICAgMSAvIDE1LAogICAgICAxIC8gOAogICAgKSwKICAgIGFiZXJ0dXJhID0gYygyLjgsIDQsIDUuNiwgOCwgMTEsIDE2LCAyMiwgMzIpCiAgKQogIAogIGRmCiAgYGBgCgoqIENvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbzoKCiAgICBgYGB7cn0KICAgIGNvcihkZiR2ZWxvY2lkYWRlLCBkZiRhYmVydHVyYSkKICAgIGBgYAoKKiBHcsOhZmljbzoKCiAgICBgYGB7cn0KICAgIGRmICU+JSAKICAgICAgZ2dwbG90KGFlcyh2ZWxvY2lkYWRlLCBhYmVydHVyYSkpICsKICAgICAgICBnZW9tX3BvaW50KCkKICAgIGBgYAoKKiBWYW1vcyB0cmFuc2Zvcm1hciBvcyB2YWxvcmVzIGRlIHVtYSBkYXMgdmFyacOhdmVpcyBwYXJhIHRvcm5hciBhIGNvcnJlbGHDp8OjbyBtYWlzIGxpbmVhciAobWVub3MgY3VydmEpOgoKICAgIGBgYHtyfQogICAgZGYgPC0gZGYgJT4lIAogICAgICBtdXRhdGUocXVhZF9hYmVydHVyYSA9IGFiZXJ0dXJhXjIpCiAgICBgYGAKICAgIAogICAgYGBge3J9CiAgICBkZiAlPiUgCiAgICAgIGdncGxvdChhZXModmVsb2NpZGFkZSwgcXVhZF9hYmVydHVyYSkpICsKICAgICAgICBnZW9tX3BvaW50KCkgKwogICAgICAgIGxhYnMoeSA9ICdhYmVydHVyYcKyJykKICAgIGBgYAoKKiBBIGNvcnJlbGHDp8OjbyBzZSB0b3JuYQoKICAgIGBgYHtyfQogICAgY29yKGRmJHZlbG9jaWRhZGUsIGRmJHF1YWRfYWJlcnR1cmEpCiAgICBgYGAKCiMjIEZ1bsOnw7VlcyB1c2FkYXMgZW0gdHJhbnNmb3JtYcOnw7VlcwoKKiAkZih4KSA9IHheMiQ6IMO6dGlsIHF1YW5kbyBvcyB2YWxvcmVzIG9yaWdpbmFpcyB0w6ptIGNhdWRhIGxvbmdhIMOgIGVzcXVlcmRhLCBvdSBxdWFuZG8gb3MgdmFsb3JlcyBvcmlnaW5haXMgdMOqbSBjb25jYXZpZGFkZSBwYXJhIGJhaXhvLgoKKiAkZih4KSA9IFxzcXJ0e3h9JDogw7p0aWwgcGFyYSB2YWxvcmVzIHF1ZSByZXByZXNlbnRhbSBjb250YWdlbnMuCgoqICRmKHgpID0gXGxvZyB4JDogw7p0aWwgcGFyYSB2YWxvcmVzIHBvc2l0aXZvcywgcXVlIGNyZXNjZW0gZGUgYWNvcmRvIGNvbSBwZXJjZW50YWdlbnMsIGNvbW8gc2Fsw6FyaW9zLCBwb3B1bGHDp8O1ZXMgZXRjLiBDb21vICRcbG9nIDAkIG7Do28gw6kgZGVmaW5pZG8sIHZvY8OqIHBvZGUgcHJlY2lzYXIgYWNyZXNjZW50YXIgdW1hIGNvbnN0YW50ZSAkXHZhcmVwc2lsb24kIGFvcyB2YWxvcmVzIGFudGVzIGRhIHRyYW5zZm9ybWHDp8Ojby4KCiogJGYoeCkgPSAtXGZyYWMxe1xzcXJ0e3h9fSQ6IG8gc2luYWwgbmVnYXRpdm8gbWFudMOpbSBhIG9yZGVuYcOnw6NvIGRvcyB2YWxvcmVzLgoKKiAkZih4KSA9IC1cZnJhYzF4JDogw7p0aWwgcGFyYSByYXrDtWVzLCBjb21vIGttL2guIE8gc2luYWwgbmVnYXRpdm8gbWFudMOpbSBhIG9yZGVuYcOnw6NvIGRvcyB2YWxvcmVzLiBDb21vICR5LzAkIG7Do28gw6kgZGVmaW5pZG8sIHZvY8OqIHBvZGUgcHJlY2lzYXIgYWNyZXNjZW50YXIgdW1hIGNvbnN0YW50ZSAkXHZhcmVwc2lsb24kIGFvcyB2YWxvcmVzIGFudGVzIGRhIHRyYW5zZm9ybWHDp8Ojby4KCgojIyBFeGVyY8OtY2lvCgoqIEFwbGlxdWUgYXMgb3V0cmFzIHRyYW5zZm9ybWHDp8O1ZXMgZGEgbGlzdGEgYWNpbWEgYW9zIHZhbG9yZXMgZGEgdmFyacOhdmVsIGBhYmVydHVyYWAuIERlc2VuaGUgZ3LDoWZpY29zLCBjYWxjdWxlIGNvcnJlbGHDp8O1ZXMsIGUgY29tcGFyZSBvcyByZXN1bHRhZG9zLgoKCiMjIEV4ZW1wbG86IHBsYW5ldGFzCgoqIEEgZGlzdMOibmNpYSBtw6lkaWEgZGUgdW0gcGxhbmV0YSBhbyBTb2wsIGVtIG1pbGjDtWVzIGRlIGttLCBlc3TDoSBjb3JyZWxhY2lvbmFkYSBjb20gYSBwb3Npw6fDo28gZG8gcGxhbmV0YSBuYSBzZXF1w6puY2lhLCBtYXMgY29tbz8KCiAgYGBge3IgZWNobz1GQUxTRX0KICBwbGFuZXRhcyA8LSB0cmliYmxlKAogICAgfm5vbWUsIH5wb3Npw6fDo28sIH5kaXN0w6JuY2lhLAogICAgJ01lcmPDunJpbycsIDEsIDM2LAogICAgJ1bDqm51cycsIDIsIDY3LAogICAgJ1RlcnJhJywgMywgOTMsCiAgICAnTWFydGUnLCA0LCAxNDIsCiAgICAnSsO6cGl0ZXInLCA1LCA0ODQsCiAgICAnU2F0dXJubycsIDYsIDg4NywKICAgICdVcmFubycsIDcsIDE3ODQsCiAgICAnTmV0dW5vJywgOCwgMjc5NiwKICAgICdQbHV0w6NvJywgOSwgMzY2NgogICkgJT4lIAogICAgbXV0YXRlKAogICAgICBkaXN0w6JuY2lhID0gZGlzdMOibmNpYSAqIDEuNgogICAgKQogIAogIHBsYW5ldGFzCiAgYGBgCgoqIENvZWZpY2llbnRlIGRlIGNvcnJlbGHDp8OjbzoKCiAgICBgYGB7cn0KICAgIGNvcihwbGFuZXRhcyRwb3Npw6fDo28sIHBsYW5ldGFzJGRpc3TDom5jaWEpCiAgICBgYGAKCiogR3LDoWZpY286CgogICAgYGBge3J9CiAgICBwbGFuZXRhcyAlPiUgCiAgICAgIGdncGxvdChhZXMocG9zacOnw6NvLCBkaXN0w6JuY2lhKSkgKwogICAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDE6OSkgKwogICAgICAgIGxhYnMoCiAgICAgICAgICB5ID0gJ2Rpc3TDom5jaWFcbihtaWxow7VlcyBrbSknCiAgICAgICAgKQogICAgYGBgCgoqIFZhbW9zIHRyYW5zZm9ybWFyIG9zIHZhbG9yZXMgZGFzIGRpc3TDom5jaWFzOgoKICAgIGBgYHtyfQogICAgcGxhbmV0YXMgPC0gcGxhbmV0YXMgJT4lIAogICAgICBtdXRhdGUobGRpc3QgPSBsb2cxMChkaXN0w6JuY2lhKSkKICAgIGBgYAogICAgCiAgICBgYGB7cn0KICAgIHBsYW5ldGFzICU+JSAKICAgICAgZ2dwbG90KGFlcyhwb3Npw6fDo28sIGxkaXN0KSkgKwogICAgICAgIGdlb21fcG9pbnQoKSArCiAgICAgICAgc2NhbGVfeF9jb250aW51b3VzKGJyZWFrcyA9IDE6OSkgKwogICAgICAgIGxhYnMoCiAgICAgICAgICB5ID0gJ2xvZyBkaXN0w6JuY2lhXG4obWlsaMO1ZXMga20pJwogICAgICAgICkKICAgIGBgYAoKKiBBIGNvcnJlbGHDp8OjbyBzZSB0b3JuYQoKICAgIGBgYHtyfQogICAgY29yKHBsYW5ldGFzJHBvc2nDp8OjbywgcGxhbmV0YXMkbGRpc3QpCiAgICBgYGAKCgojIyBFeGVyY8OtY2lvCgoqIEFwbGlxdWUgYXMgb3V0cmFzIHRyYW5zZm9ybWHDp8O1ZXMgZGEgbGlzdGEgYWNpbWEgYW9zIHZhbG9yZXMgZGEgdmFyacOhdmVsIGBkaXN0w6JuY2lhYC4gRGVzZW5oZSBncsOhZmljb3MsIGNhbGN1bGUgY29ycmVsYcOnw7VlcywgZSBjb21wYXJlIG9zIHJlc3VsdGFkb3MuCgoKCgo8ZGl2IHN0eWxlPSdoZWlnaHQ6IDEwMDBweCc+PC9kaXY+Cg==